home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Night Owl 6
/
Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso
/
016a
/
4d_while.zip
/
WHILE.TXT
< prev
next >
Wrap
Text File
|
1991-11-18
|
11KB
|
241 lines
Whiling Away in 4DOS
by
Marshall E. Giguere
November 17, 1991
If you're familiar with the constructions supported by structured
programming languages you'll be used to having available things like
"while", "repeat" etc. in the language. This is not, however, the
case where DOS COMMAND.COM and 4DOS are concerned. DOS has no
support for any structured language constructions and 4DOS supports
only "if/then/else" constructs. Although neither language supports
the "while" construct a "while" can be simulated, and in the case of
4DOS formalized into a convention supported by a couple of simple
aliases. That's what this little article is about.
What to do When You don't have a WHILE!
If you're working with a language like 4DOS that doesn't have a
"while" you can simulate the behavior of a "while" by remembering
the following things about a while:
* logical testing happens at the top of the loop.
* the loop only executes if the test is TRUE.
* at the end of the code block you always branch
back to the top.
With these three simple rules you can write a "while" loop using
nothing but "if" and "goto" (yes, the dreaded & dangerous GOTO).
The result is less than appealing, and is certainly confusing to the
eye of the reader. If, God forbid, you're working in DOS COMMAND.COM
and you need to repeat a section of code over which some condition
holds true the result is truly horrific. Let's say you need to
iterate over a block of code while the variable "i" is less than ten,
here's what the DOS COMMAND.COM equivalent would look like:
:loop
if %i GT 10 GOTO endloop
.
.
do something interesting
set i=%@eval[%i + 1]
.
.
goto loop
:endloop
Notice that it requires the creation of two labels to make an
equivalent "while" in DOS COMMAND.COM. You can achieve the same
result in 4DOS at the expense of only one label with an "if/then"
(see below).
:loop
iff %i LT 10 then
.
.
do something interesting
set i=%@eval[%i + 1]
.
.
goto loop
endiff
*NOTE: it is assumed that "i" is initialized to some reasonable
value before entering the loop.
You'll notice immediately that the resulting 4DOS code is a bit
easier on the eye and saved one label. The above code still, to my
mind, does not convey the directly fact that an iteration is in
progress until one has scanned the entire block of code up to the
"goto loop" statement, it's still just an "if" statement.
As you can see we can achieve in effect the semantics of a "while"
using nothing but "if" and "goto". It is also possible to do the
same for the "repeat/until" or "do/while" constructs by moving the
logical tests to the bottom of the loop, that is left as an exercise
for the reader. The next logical question is "how can we have the
syntax and the semantics of iteration in 4DOS", and that is the
subject of the next section.
WHILE You Wait
Before we begin constructing of a "while" language element for 4DOS I
should probably discuss, briefly, why go through this exercise
anyway, and discuss some design goals to be achieved. Then we can
proceed directly to the implementation of the WHILE construct.
Why invent a WHILE language element at all is probably the question
on your mind. After all in the preceding section I proved
conclusively that a WHILE can be simulated with the basic language
elements "testing" and "branching". Isn't that sufficient? The
answer is yes it's sufficient, but no, it doesn't convey the direct
semantics of the operation "while". We must examine the "if/goto"
code block in some detail to determine that an iteration on some
condition is in effect. With a "while" construction the semantics of
the conditional iteration are readily apparent, i.e. the syntax and
the semantics are in harmony. By this I simply mean that the
conveyed and computed meaning of a "while" construction are identical.
Whenever possible programming language designers have an obligation
to assure the user that the conveyed and computed semantics are in
harmony. Having established that linguistic harmony is a reasonable
and useful goal let's look at the implementation goals next.
Implementation of a "while" construction for 4DOS should encompass a
few simple goals and ideas:
1. the "while" should be clean as possible.
2. permit the full complement of all possible 4DOS
conditionals.
3. be reasonably simple to use.
4. be nestable.
The above goals all come into conflict when we consider that in order
to implement a "while" it is necessary at the end of the loop to
branch back to the beginning. A branch requires that we know in
advance where it is intended that the end of the loop branch back to,
this can only be resolved through the use of a label statement. The
obvious question here is how will such a label be generated, and
unfortunately the answer is that you the programmer must generate the
label manually. This is the one ugliness I've had to endure for the
clarity I desire. The problem then is how to incorporate the
labeling into the methodology without creating something really ugly.
My goal is still to create something reasonably pleasing to the eye.
Since we must declare the branch back label I decided to make a
virtue of the necessity and incorporate the requirement into the
language elements. Item 2 above is easily accommodated with the 4DOS
"iff" which covers a host of possible conditionals. The final
result, I hope you'll agree, meets goal 3, simplicity of use.
Obviously we need to know two things for the "while" loop to work
properly: where does it start and where does it end. These functions
are accomplished by introducing two new 4DOS language elements (in
the form of aliases) "while" and "endwhile". The "while" alias
starts things off be declaring the conditional and bracketing the
beginning of the code block to be executed if the condition is valid.
The "endwhile" must serve two purposes: force an unconditional branch
to the beginning of the loop and act as the closure for when
(eventually) the "while" conditional evaluates to false, i.e. this is
where you go when the loop is complete. Here are the two aliases for
implementing the "while" loop in 4DOS:
WHILE=`iff %& then`
ENDW*HILE=`goto %1^endiff`
As you can see the "WHILE" alias is a disguised "iff" which will
swallow any string and treat it as a conditional expression. The
"then" clause of the "iff" opens the code block, and this is where
you supply any code plus code to modify the conditional variable(s)
so the loop reaches closure. The "ENDWHILE" alias takes one
argument the name of the label which bounds the top of the loop and
supplies the needed "endiff" to allow 4DOS to find the closure point
when the "while" conditional finally fails. Obviously just using
these aliases as is isn't quite enough, remember I said you had to
supply the label to bound the loop. This simply means at the top of
the loop you must declare the label that will be used to control the
unconditional "goto" in the "endwhile". The label you declare at the
top of the "while" and the one in the "endwhile" must be the same.
Here's where necessity becomes virtue. By declaring the label at the
top and bottom of the "while" some documentation is built into the
language, making it obvious which "endwhile" belongs to which
"while", this is especially useful in a nested "while" situation.
Let's write a simple "while" that prints the numbers from 0 to 10
using the new "WHILE" aliases.
@echo off
set i=0
:while1
while %i LE 10
echo i = %i
set i=%@eval[%i+1]
endwhile while1
As you see in the above code the "while" begins with the declaration
of the top bounding label ":while1". Next we see the "while" with its
conditional expression which states that the loop executes "while"
"%i" is less than or equal to ten. The "endwhile" declares the end
of the "while" and tells us (and 4DOS) where to go at the end of each
pass through the loop, in this case the label ":while1". Sandwiched
in between is the code block which is executed on every pass through
the loop. The result, I hope you'll agree, isn't too ugly and does
what I intended, i.e. conveys the semantics and the syntax of a
"while" loop.
Restrictions
Since the "while" aliases are in fact a 4DOS "iff/then^endiff" all
the same restrictions apply to nesting. You cannot nest more that
eight levels of "iff" or "while" in any combination. Further to
avoid dangling "iff" closures you should close each "iff" with an
"endiff". This will prevent 4DOS from becoming clinically confused
and leaping as it were to the wrong "conclusion" (endiff).
Portability
The problem with building language constructs out of aliases/macros
is the same faced by code library builders. That simply is that you
must distribute your "library" with your code. The same is true
here, you must hand-out the "while" aliases with any 4DOS batch file
you create that uses them. This can be a painful problem since one
tends to forget something that in effect becomes part of the
language. This is especially true if a macro fits into the native
language so well that you forget it is just that an macro, you take
it for granted. All this just goes to say that any batch files you
create using the "while/endwhile" aliases will require that the
aliases accompany the batch file.
Concluding remarks
Although the "while/endwhile" isn't perfect meaning labels must be
declared the result isn't too ugly and the obligatory labels provide
some use as documentation. I tried all kinds of ideas out to rid
myself of the necessity of physically declaring the "while" labels,
but 4DOS' mechanism for resolving labels is too simple-minded, i.e.
4DOS does a simple textual search with no expansion for labels. This
simply means you can't created a computed label, or hide one in an
alias. But, in the mean time the affect is achievable with aliases.
Maybe someday 4DOS will actually implement a "while" and other
structured programming language constructs? The problem that must be
addressed by the implementers of 4DOS is the same one I encountered,
i.e. how to manufacture and track labels. One is tempted to suggest
the use of byte offset signatures to obviate the need to declare
labels. Simply put when a "while" is encountered its byte-offset is
pushed onto a context stack. When and "endwhile" is encountered it
is likewise pushed onto the context stack and the last pushed "while"
offset is used to get back to the most resent "while". If the while
continues its "endwhile" is popped and execution continues through
the loop, if the "while" fails the "while" and "endwhile" offsets are
popped and a jump to the statement following the "endwhile" offset is
executed. This is a fully reenterant process and may be implemented
to any reasonable number of levels. Hey, Rex, it's only a suggestion.